package cn.itamt.utils {
	import flash.display.BitmapData;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;

	public class PNGEncoder {

		public static function encode(img : BitmapData, type : uint = 0) : ByteArray {
	    	
			// Create output byte array
			var png : ByteArray = new ByteArray();
			// Write PNG signature
			png.writeUnsignedInt(0x89504e47);
			png.writeUnsignedInt(0x0D0A1A0A);
			// Build IHDR chunk
			var IHDR : ByteArray = new ByteArray();
			IHDR.writeInt(img.width);
			IHDR.writeInt(img.height);
			if(img.transparent || type == 0) {
				IHDR.writeUnsignedInt(0x08060000); // 32bit RGBA
			} else {
				IHDR.writeUnsignedInt(0x08020000); //24bit RGB
			}
			IHDR.writeByte(0);
			writeChunk(png, 0x49484452, IHDR);
			// Build IDAT chunk
			var IDAT : ByteArray = new ByteArray();
	        
			switch(type) {
				case 0:
					writeRaw(img, IDAT);
					break;
				case 1:
					writeSub(img, IDAT);
					break;
			}
	        
			IDAT.compress();
			writeChunk(png, 0x49444154, IDAT);
			// Build IEND chunk
			writeChunk(png, 0x49454E44, null);
			// return PNG

			
			
			return png;
		}

		private static function writeRaw(img : BitmapData, IDAT : ByteArray) : void {
			var h : int = img.height;
			var w : int = img.width;
			var transparent : Boolean = img.transparent;
	        
			for(var i : int = 0;i < h;i++) {
				// no filter
				if ( !transparent ) {
					var subImage : ByteArray = img.getPixels(new Rectangle(0, i, w, 1));
					//Here we overwrite the alpha value of the first pixel
					//to be the filter 0 flag
					subImage[0] = 0;
					IDAT.writeBytes(subImage);
					//And we add a byte at the end to wrap the alpha values
					IDAT.writeByte(0xff);
				} else {
					IDAT.writeByte(0);
					var p : uint;
					for(var j : int = 0;j < w;j++) {
						p = img.getPixel32(j, i);
						IDAT.writeUnsignedInt(uint(((p & 0xFFFFFF) << 8) | (p >>> 24)));
					}
				}
			}
		}

		private static function writeSub(img : BitmapData, IDAT : ByteArray) : void {
			var r1 : uint;
			var g1 : uint;
			var b1 : uint;
			var a1 : uint;
            
			var r2 : uint;
			var g2 : uint;
			var b2 : uint;
			var a2 : uint;
            
			var r3 : uint;
			var g3 : uint;
			var b3 : uint;
			var a3 : uint;
            
			var p : uint;
			var h : int = img.height;
			var w : int = img.width;
	        
			for(var i : int = 0;i < h;i++) {
				// no filter
				IDAT.writeByte(1);
				if ( !img.transparent ) {
					r1 = 0;
					g1 = 0;
					b1 = 0;
					a1 = 0xff;
					for(var j : int = 0;j < w;j++) {
						p = img.getPixel(j, i);
	                    
						r2 = p >> 16 & 0xff;
						g2 = p >> 8 & 0xff;
						b2 = p & 0xff;
	                    
						r3 = (r2 - r1 + 256) & 0xff;
						g3 = (g2 - g1 + 256) & 0xff;
						b3 = (b2 - b1 + 256) & 0xff;
	                    
						IDAT.writeByte(r3);
						IDAT.writeByte(g3);
						IDAT.writeByte(b3);
	                    
						r1 = r2;
						g1 = g2;
						b1 = b2;
						a1 = 0;
					}
				} else {
					r1 = 0;
					g1 = 0;
					b1 = 0;
					a1 = 0;
					for(var j : int = 0;j < w;j++) {
						p = img.getPixel32(j, i);
	                    
						a2 = p >> 24 & 0xff;
						r2 = p >> 16 & 0xff;
						g2 = p >> 8 & 0xff;
						b2 = p & 0xff;
	                    
						r3 = (r2 - r1 + 256) & 0xff;
						g3 = (g2 - g1 + 256) & 0xff;
						b3 = (b2 - b1 + 256) & 0xff;
						a3 = (a2 - a1 + 256) & 0xff;
	                    
						IDAT.writeByte(r3);
						IDAT.writeByte(g3);
						IDAT.writeByte(b3);
						IDAT.writeByte(a3);
	                    
						r1 = r2;
						g1 = g2;
						b1 = b2;
						a1 = a2;
					}
				}
			}
		}

		private static var crcTable : Array;
		private static var crcTableComputed : Boolean = false;

		private static function writeChunk(png : ByteArray, type : uint, data : ByteArray) : void {
			if (!crcTableComputed) {
				crcTableComputed = true;
				crcTable = [];
				for (var n : uint = 0;n < 256;n++) {
					var c : uint = n;
					for (var k : uint = 0;k < 8;k++) {
						if (c & 1) {
							c = uint(uint(0xedb88320) ^ uint(c >>> 1));
						} else {
							c = uint(c >>> 1);
						}
					}
					crcTable[n] = c;
				}
			}
			var len : uint = 0;
			if (data != null) {
				len = data.length;
			}
			png.writeUnsignedInt(len);
			var p : uint = png.position;
			png.writeUnsignedInt(type);
			if ( data != null ) {
				png.writeBytes(data);
			}
			var e : uint = png.position;
			png.position = p;
			var c : uint = 0xffffffff;
			for (var i : int = 0;i < (e - p);i++) {
				c = uint(crcTable[
	                (c ^ png.readUnsignedByte()) & 0xff] ^ (c >>> 8));
			}
			c = uint(c ^ uint(0xffffffff));
			png.position = e;
			png.writeUnsignedInt(c);
		}
	}
}
